﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AntlrTS.LangFundmental
{
    public interface ITypeInferenceVisitor
    {
        TypeSubstile Visit(ProductionConstructor pro);
        TypeSubstile Visit(Constant con);
        TypeSubstile Visit(TypeVariable tv);
        TypeSubstile Visit(TypeConstructors tc);

        void Visit(Pattern pattern);

        void Visit(ConstructorPattern constructorPattern);
    }


    public class LangObject
    {
        public LangObject() { }

        public LangObject(string name) { rawStringName = name; }

        public static implicit operator string(LangObject l)
        {
            return l.toRawString();
        }

        public string rawStringName = "";
        public virtual string toRawString(){return rawStringName;}
    }

    public class TSTypeAST:LangObject
    {
        public TSTypeAST(string name)
        {
            if(typeNameAndId.ContainsKey(name))
            {
                typeRawString = name;
                rawStringName = name;
                return;
            }
            typeRawString =rawStringName= name;
            uniqueId=ro.Next();
            while(uniques.Contains(uniqueId))
            {
                uniqueId = ro.Next();
            }
            allTypes.Add(uniqueId, this);
            typeNameAndId.Add(name, uniqueId);
        }

        public TSTypeAST getThis()
        {
            return allTypes[typeNameAndId[typeRawString]];
        }
        
        public bool isBuildInType = false;
        public bool userDefinedType = false;
        public bool isBottomType = false;
        public bool isTopType = false;
        public bool isNeedInference = true;
        public string typeRawString = "";
        public Int64 uniqueId = 0;
        public static HashSet<Int64> uniques = new HashSet<Int64>();
        public static Dictionary<string, Int64> typeNameAndId = new Dictionary<string, long>();
        public static Dictionary<Int64, TSTypeAST> allTypes = new Dictionary<long, TSTypeAST>(); 
        static Random ro = new Random();

        public override string toRawString()
        {
            return rawStringName;
        }
    }

    public class TypeSubstile:LangObject
    {
        public List<TSTypeAST> types = new List<TSTypeAST>();
        public List<List<TSTypeAST>> realTypes = new List<List<TSTypeAST>>();
        public override string toRawString()
        {
            if(types.Count==0)
            {
                return "Need type inference.";
            }
            string typeSTs = "";
            for (int i = 0; i < types.Count-1; i++)
            {
                typeSTs += types[i].toRawString()+"->";
            }
            typeSTs += types[types.Count - 1];
            return typeSTs;
        }
    }

    public class Variable : LangObject
    {
        public bool isConstant = false;
        public bool isFunction = false;
        public Variable(string name) { rawStringName = name; }
        public Variable() { }
        public override string toRawString()
        {
            return rawStringName;
        }
    }

    public class TypeScheme:LangObject
    {
        public TypeScheme(string name)
        {
            rawStringName = name;
        }

        public override string toRawString()
        {
            return "TypeScheme:" + rawStringName;
        }
    }

    public class TypeVariable:Variable
    {
        public TypeVariable(string name)
        {
            rawStringName=name;
        }

        public TypeSubstile types=new TypeSubstile();

        public override string toRawString()
        {
            return "TypeVariable:" + rawStringName+"\n"+types.toRawString();
        }

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class Constant : Variable
    {
        public Constant(Int64 intv)
        {
            m = ConstanType.INT;
            intValue = intv;
        }

        public Constant(string stringv)
        {
            m = ConstanType.STRING;
            stringValue = stringv;
        }

        public Constant(double doublev)
        {
            m = ConstanType.FLOAT;
            doubleValue = doublev;
        }

        public Constant(char charv)
        {
            m = ConstanType.CHAR;
            charValue = charv;
        }

        public Constant(bool p)
        {
            // TODO: Complete member initialization
            m = ConstanType.BOOL;
            this.boolValue = p;
        }
        public enum ConstanType { CHAR, STRING, INT, FLOAT ,BOOL}
        ConstanType m;
        public ConstanType getType() { return m; }
        public Int64 intValue;
        public string stringValue;
        public char charValue;
        public double doubleValue;
        public bool boolValue;

        public override string toRawString()
        {
            switch (m)
            {
                case ConstanType.CHAR:
                    return "Char type:" + charValue;
                case ConstanType.STRING:
                    return "String type:" +stringValue;
                case ConstanType.INT:
                    return "Int type:" + intValue.ToString();
                case ConstanType.FLOAT:
                    return "Float type:" +doubleValue.ToString();
                case ConstanType.BOOL:
                    return "Bool type:" + boolValue.ToString();
                default:
                    break;
            }
            return "Error constant type";
        }

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }

    }

    public class Pattern :LangObject
    {
        /// <summary>
        /// 常量 或者 构造模式
        /// </summary>
        public List<LangObject> constantOrConstructorPattern = new List<LangObject>();
        public bool isOtherWise
        {
            get
            {
                return constantOrConstructorPattern.Count == 0;
            }
        }

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }

    }

    public class ConstructorPattern:LangObject
    {
        /// <summary>
        /// constructor type
        /// </summary>
        public ProductionConstructor proconstructor;


        public string constructorName = "";

        /// <summary>
        /// 参数  和 常量 和 构造模式
        /// </summary>
        public List<Variable> constantOrVariables = new List<Variable>();

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class ProductionConstructor:LangObject
    {
        /// <summary>
        /// sum type 
        /// </summary>
        public TSTypeAST fatherType;

        /// <summary>
        /// pro type
        /// </summary>
        public TSTypeAST type;

        /*TypeScheme or TSType*/
        public List<LangObject> typeSchemeOrTSType = new List<LangObject>();

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class TypeConstructors:LangObject
    {
        /// <summary>
        /// sum type
        /// </summary>
        public TSTypeAST type;
        /// <summary>
        /// typeschemes 
        /// </summary>
        public List<TypeScheme> typeSchemes = new List<TypeScheme>();

        /// <summary>
        /// pro 构造器
        /// </summary>
        public List<ProductionConstructor> productionConstructor = new List<ProductionConstructor>();

        public void Accept(ITypeInferenceVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class FunctionAnnoation:LangObject
    {
        public FunctionAnnoation(string name)
        {
            rawStringName = name;
        }

        public TypeSubstile substile = new TypeSubstile();
    }

    public class ListPatterns:LangObject
    {
        public List<LangObject> patterns = new List<LangObject>();
    }

    public class ContantWithVariable:LangObject
    {
        public List<Variable> contantWithVariables=new List<Variable>();
    }

    public class ListAssignVariable:LangObject
    {
        public List<TSFunction> tsfs = new List<TSFunction>();
    }

    public class FunctionName:LangObject
    {
        public List<string> moduleName=new List<string>();
        public string functionName="";
    }

    public class ProductionCones:LangObject
    {
        public List<ProductionConstructor> pcs = new List<ProductionConstructor>();
    }
}
